#!/usr/bin/perl
#
#	$HeadURL$
#	$Revision$
#	$Date$
#

use strict;
use warnings;

use IO::Socket::INET;
use Digest::MD5 qw(md5_hex);
#use Digest::SHA qw(sha256_hex);
use Time::HiRes qw(gettimeofday);

#	Print out the syntax if we don't get what we need.
unless (@ARGV && $ARGV[0]) {
	print "Usage: $0 <port> [password] [adminpass]\n";
	exit 1;
}

#	Running configuration.
my $port = $ARGV[0] || 5000;
my $password = $ARGV[1] || 'password';
my $reset_pw = $ARGV[2] || 'resetpw';

#	Status is a global, but can be changed later.
my $status = 'OK';
my $detail = '';

#	Create a socket to listen on.
my $socket = new IO::Socket::INET (
	LocalHost => '127.0.0.1',
	LocalPort =>  $port,
	Proto => 'tcp',
	Listen => 5,
	Reuse => 1
	) or die "FATAL: Could not create socket: $!\n";

print "Waiting for client connection on port $port.\n";

#	Start accepting clients.
while (my $client_socket = $socket->accept()) {
	$client_socket->autoflush(1);
	my $peer_address = $client_socket->peerhost();
	my $peer_port = $client_socket->peerport();

	print "Accepted New Client Connection From : $peer_address, $peer_port\n";

	my $nonce = undef;

	# read operation on the newly accepted client
	my $data = undef;
	while (<$client_socket>) {
		$data = $_;

		if ($data) {
			chomp $data;
#			print $client_socket "$data\n";
			print "Received from Client: $data\n";

			#	Send the nonce.
			if ($data eq 'REQ_NONCE') {
				$nonce = join '', gettimeofday();
				print "Sending: NONCE $nonce\n";
				print $client_socket "NONCE $nonce\n";
			}

			#	Attempt to authenticate the user.
			if ($data =~ /^REQ_STATUS user/) {
			#if ($data =~ /^REQ_STATUS user $cnonce $hash/) {
				if (authenticated($data,$nonce,$password)) {
					print "Sending status: STATUS $status $detail\n";
					print $client_socket "STATUS $status $detail\n";
				} else {
					print "Sending status: Bad authentication\n";
					print $client_socket "STATUS BAD AUTH\n";
				}
			}

			#	Allow for setting detail, in the event that Booleans aren't
			#	enough.
			if ($data =~ /^SET_STATUS\((.*)\) (.*)$/) {
				my $reqDetail = $1;
				if (authenticated($data,$nonce,$reset_pw)) {
					$detail = $reqDetail;
					print "Setting detail to |$detail|\n";
					print $client_socket "STATUS $status $detail\n";
				}
			}

			#	Allow admin resets, will double for test apparatus.
			if ($data =~ /^TOGGLE_STATUS (.*)$/) {
				if (authenticated($data,$nonce,$reset_pw)) {
					my $oldStatus = $status;
					if ($oldStatus eq 'OK') {
						$status = 'BAD';
					} else {
						$status = 'OK';
					}
					print "Toggled status from $oldStatus to $status.\n";
					print $client_socket "STATUS $status $detail\n";
				}
			}

			#	Client is done with us, we can close.
			if ($data =~ /^QUIT/) {
				print "Closing client socket.\n";
				last;
			}
		}
	}
	close $client_socket;
}

$socket->close();

#
#	Authenticate the user.
#
sub authenticated {
	my $authString = shift;
	my $nonce = shift;
	my $password = shift;

	my ($cmd,$user,$cnonce,$chash) = split /\s/, $authString;

	#	We need all of the arguments intact, else we must fail.
	unless ($cmd && $user && $cnonce && $chash && $nonce && $password) {
		return 0;
	}

	#	Calculate what we think the hash ought to be.
	my $hash = md5_hex($nonce.$cnonce.$password);
	#my $auth_hash = sha256_hex($nonce.$cnonce.$password);

	if ($hash eq $chash) {
		print "Authenticated as $user.\n";
		return 1;
	}
}

# EOF
